package com.fy.utilsproject.utils.math;

import java.math.BigDecimal;

/**
 * 计算工具类，直接使用float、double等进行计算不准确，所以封装此类用BigDecimal来做精准计算
 */
public class CalculationUtil {


    public static void main(String[] args) {
        //FIXME 只为便捷调试使用，正式代码要去掉
//        calculateTimeWhenVTo0(20, -1.5);
//        calculateDistanceWhenVTo0(20, -1.5);
//        tan(20, 30);
//        tan(20, -30);

//        for (double i = -1; i < 1; i+=0.01) {
//            System.out.println(""+Math.pow(2.718282,-i*i/2.0));
//        }

            System.out.println(312L*176245896/3.066E8+"");

    }

    /**
     * s = v0 * t + 1/2 * a * t²
     *
     * @param v0 初速度
     * @param a  加速度
     * @param t  时间
     * @return 位移
     */
    public static double calculateDistance(double v0, double a, double t) {
        BigDecimal v0B = BigDecimal.valueOf(v0);
        BigDecimal aB = BigDecimal.valueOf(a);
        BigDecimal tB = BigDecimal.valueOf(t);
        BigDecimal v0t = v0B.multiply(tB);
        BigDecimal att = aB.multiply(tB.multiply(tB));
        BigDecimal s = v0t.add(divide(att, BigDecimal.valueOf(2), 2));
        return s.doubleValue();
    }

    /**
     * 根据初速度、加速度和位移计算消耗时间
     *
     * @param v0 初速度
     * @param a  加速度
     * @param s  位移
     * @return 时间
     */
    public static double calculateTime(double v0, double a, double s) {
        System.out.println("calculateTime->v0:" + v0 + ",a,:" + a + ",s:" + s);
        BigDecimal v0B = BigDecimal.valueOf(v0);
        BigDecimal aB = BigDecimal.valueOf(a);
        BigDecimal sB = BigDecimal.valueOf(s);
        System.out.println("calculateTime->v0B:" + v0B + ",aB:" + aB + ",sB:" + sB);
        BigDecimal v02 = v0B.multiply(v0B);
        System.out.println("calculateTime->v02:" + v02);
        BigDecimal as_2 = aB.multiply(sB).multiply(BigDecimal.valueOf(2));
        System.out.println("calculateTime->as_s:" + as_2);
        double kaifang = Math.sqrt(v02.add(as_2).doubleValue());
        System.out.println("calculateTime->kaifang:" + kaifang);
        double beichushu = kaifang - v0;
        System.out.println("calculateTime->beichushu:" + beichushu);
        double t;
        if (a != 0) {
            t = (kaifang - v0) / a;
        } else {
            t = s / v0;
        }
        System.out.println("calculateTime->t:" + t);
        return t;
//        double temp = Math.sqrt(v0B.multiply(v0B).add(aB.multiply(sB).multiply(BigDecimal.valueOf(2))).doubleValue()) - v0;
//        double time = divide(temp, v0, 3);
//        System.out.println("calculateTime->time:" + time);
//        return time;
    }

    /**
     * 通过勾股定理计算斜边长度
     *
     * @param a 直角边a
     * @param b 直角边b
     * @return 返回斜边长度
     */
    public static double calculateHypotenuseByPythagorean(double a, double b) {
        BigDecimal aB = BigDecimal.valueOf(a);
        BigDecimal bB = BigDecimal.valueOf(b);
        double hypotenuse = Math.sqrt(aB.multiply(aB).add(bB.multiply(bB)).doubleValue());
        System.out.println("calculateHypotenuseByPythagorean->hypotenuse:" + hypotenuse);
        return hypotenuse;
    }

    /**
     * 根据余弦计算弦的长度
     *
     * @param a     边长
     * @param angle 角度
     * @return 返回弦长
     */
    public static double calculateHypotenuseByCos(double a, double angle) {
        double tan = multiply(a, Math.cos(Math.toRadians(angle)));
        System.out.println("calculateHypotenuseByCos->tan:" + tan);
        return tan;
    }

    public static double sin(double a, double angle) {
        double sin = multiply(a, Math.sin(Math.toRadians(angle)));
        System.out.println("sin->sin:" + sin);
        return sin;
    }

    public static double cos(double a, double angle) {
        double cos = multiply(a, Math.cos(Math.toRadians(angle)));
        System.out.println("cos->cos:" + cos);
        return cos;
    }

    /**
     * 计算tan值
     *
     * @param a     边长
     * @param angle 角度
     * @return 返回tan边长
     */
    public static double tan(double a, double angle) {
        double tan = multiply(a, Math.tan(Math.toRadians(angle)));
        System.out.println("tan->tan:" + tan);
        return tan;
    }

    /**
     * 计算匀减速直线运动中，当速度减到0时走过的位移
     *
     * @param v0 初速度
     * @param a  加速度，必须与初速度方向相反
     * @return 位移
     */
    public static double calculateDistanceWhenVTo0(double v0, double a) {
        if (v0 * a < 0) {
            //只有当初速度和加速度方向相反时才能使运动最终停止
            double s = divide(-multiply(v0, v0), multiply(2, a), 2);
            System.out.println("calculateDistanceWhenVTo0->s:" + s);
            return s;
        }
        return 0;
    }

    /**
     * 计算匀减速直线运动中，当速度减到0时走过的位移
     *
     * @param v0 初速度
     * @param a  加速度，必须与初速度方向相反
     * @return 位移
     */
    public static double calculateTimeWhenVTo0(double v0, double a) {
        if (v0 * a < 0) {
            //只有当初速度和加速度方向相反时才能使运动最终停止
            double time = divide(-v0, a, 2);
            System.out.println("calculateTimeWhenVTo0->time:" + time);
            return time;
        }
        return 0;
    }

    /**
     * ( 相对 )精确除法运算 . 当发生除不尽情况时 , 由scale参数指 定精度 , 以后数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位
     * @return 两个参数的商(String)
     */
    public static double divide(double v1, double v2, Integer scale) {
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return divide(b1, b2, scale).doubleValue();
    }

    /**
     * ( 相对 )精确除法运算 . 当发生除不尽情况时 , 由scale参数指 定精度 , 以后数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位
     * @return 两个参数的商(String)
     */
    public static String divide(String v1, String v2, Integer scale) {
        if (null == v1) {
            return "0";
        }
        if (null == v2) {
            v2 = "1";
        }
        BigDecimal b1 = new BigDecimal(v1.trim());
        BigDecimal b2 = new BigDecimal(v2.trim());
        return String.valueOf(divide(b1, b2, scale));
    }

    /**
     * ( 相对 )精确除法运算 . 当发生除不尽情况时 , 由scale参数指 定精度 , 以后数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位
     * @return 两个参数的商(BigDecimal)
     */
    public static BigDecimal divide(BigDecimal v1, BigDecimal v2, Integer scale) {
        if (null == v1) {
            return BigDecimal.ZERO;
        }
        if (null == v2) {
            v2 = BigDecimal.ONE;
        }

        if (v2.compareTo(BigDecimal.ZERO) == 0) {
            throw new IllegalArgumentException("除数不能为0");
        }

        if (scale < 0) {
            throw new IllegalArgumentException("精确度不能小于0");
        }

        return v1.divide(v2, scale, BigDecimal.ROUND_HALF_UP);
    }


    /**
     * 精确乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积(String)
     */
    public static double multiply(double v1, double v2) {
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return multiply(b1, b2).doubleValue();
    }


    /**
     * 精确乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积(String)
     */
    public static String multiply(String v1, String v2) {
        if (isBlank(v1)) {
            v1 = "1";
        }
        if (isBlank(v2)) {
            v2 = "1";
        }
        BigDecimal b1 = new BigDecimal(v1.trim());
        BigDecimal b2 = new BigDecimal(v2.trim());
        return String.valueOf(multiply(b1, b2));
    }

    /**
     * 精确乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积(BigDecimal)
     */
    public static BigDecimal multiply(BigDecimal v1, BigDecimal v2) {
        if (null == v1) {
            v1 = BigDecimal.ONE;
        }
        if (null == v2) {
            v2 = BigDecimal.ONE;
        }
        return v1.multiply(v2);
    }


    /**
     * 判断字符串是否为空(不依赖第三方)
     *
     * @param str 需要判断的字符串
     * @return 返回字符串是否为空
     */
    private static boolean isBlank(String str) {
        return null == str || str.trim().length() == 0;
    }


}
